﻿using System.Linq;
using nRoute.Components;
using nRoute.Navigation.Mapping;
using nRoute.Samples.Netflix.Data.Catalog;
using nRoute.Samples.Netflix.Views;
using nRoute.ViewModels;
using System;
using nRoute.Components.Composition;
using nRoute.Samples.Netflix.Data;
using System.Diagnostics;
using System.Collections;
using System.Data.Services.Client;

namespace nRoute.Samples.Netflix.ViewModels
{
    [DefineNavigationContent("Content/Netflix/Person/", typeof(PersonView))]
    [MapViewModel(typeof(PersonView))]
    public class PersonViewModel
        : NavigationViewModelBase
    {
        private const string PERSON_KEY = "Person";
        private const string TITLE_FORMAT = "Person: {0}";

        private int _loadingToken = 0;
        private NetflixDataContext _context;
        private ValueConverter<Person, DataServiceQuery<Title>> _actedInConverter;
        private ValueConverter<Person, DataServiceQuery<Title>> _directedConverter;

        [ResolveConstructor]
        public PersonViewModel(NetflixDataContext context)
        {
            _context = context;
        }

#region Properties

        public Person Person { get; private set; }

        public bool IsLoading { get; private set; }

        public ValueConverter<Person, DataServiceQuery<Title>> ActedInQueryConverter
        {
            get
            {
                if (_actedInConverter == null) _actedInConverter = CreateActedInConverter();
                return _actedInConverter;
            }
        }

        public ValueConverter<Person, DataServiceQuery<Title>> DirectedInQueryConverter
        {
            get
            {
                if (_directedConverter == null) _directedConverter = CreateDirectedInConverter();
                return _directedConverter;
            }
        }

#endregion

#region Overrides

        protected override void OnIntialize(ParametersCollection state)
        {
            // if no data is passed in on initialization, it means it will be restored from state
            if (!state.ContainsKey(PERSON_KEY)) return;
            SetupPerson((Person)state[PERSON_KEY]);
        }

        protected override void OnRestoreState(ParametersCollection state)
        {
            // if person is already loaded we don't load again
            if (Person != null) return;

            if (state.ContainsKey(PERSON_KEY))
            {
                SetupPerson((Person)state[PERSON_KEY]);
            }
            else
            {
                // inform no loaded data
            }
        }

        protected override ParametersCollection OnSaveState()
        {
            if (Person != null)
            {
                return new ParametersCollection() 
                { 
                    new Parameter(PERSON_KEY, Person)
                };
            }
            else
            {
                return null;
            }
        }

#endregion

#region Helpers

        private void SetupPerson(Person person)
        {
            base.Title = string.Format(TITLE_FORMAT, person.Name);
            this.Person = person;

            if (person.TitlesActedIn.Count == 0)
            {
                _loadingToken += 1;
                try
                {
                    IAsyncResult _result1 = null;
                    _result1 = _context.BeginLoadProperty(person, "TitlesActedIn", (i) =>
                    {
                        System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                        {
                            if (_result1 != null) _context.EndLoadProperty(_result1);
                            OnLoadCompleted();
                        }));
                    }, null);                    
                }
                catch
                {
                     // todo: handle properly
                    Debugger.Break();
                    OnLoadCompleted();
                }
            }
            
            if (person.TitlesDirected.Count == 0)
            {
                _loadingToken += 1;
                try
                {
                    IAsyncResult _result2 = null;
                    _result2 = _context.BeginLoadProperty(person, "TitlesDirected", (i) =>
                    {
                        System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
                        {
                            if (_result2 != null) _context.EndLoadProperty(_result2);
                            OnLoadCompleted();
                        }));
                    }, null);
                }
                catch
                {
                    // todo: handle properly
                    Debugger.Break();
                    OnLoadCompleted();
                }
            }

            if (_loadingToken > 0 && Person.TitlesActedIn.Count == 0 && person.TitlesDirected.Count == 0)
            {
                IsLoading = true;
                NotifyPropertyChanged(() => IsLoading);
            }
            NotifyPropertyChanged(() => Person);
        }

        private void OnLoadCompleted()
        {
            // note we use the token things to ensure we don't just the name of the actor..
            _loadingToken -= 1;
            if (_loadingToken == 0)
            {
                IsLoading = false;
                NotifyPropertyChanged(() => IsLoading);
                NotifyPropertyChanged(() => Person);
            }
        }

        private ValueConverter<Person, DataServiceQuery<Title>> CreateActedInConverter()
        {
            return new ValueConverter<Person, DataServiceQuery<Title>>((p) => (DataServiceQuery<Title>)(
                   from _p in _context.People.IncludeTotalCount()
                   from _t in _p.TitlesActedIn
                   where _p.Id == p.Id
                   select _t));
        }

        private ValueConverter<Person, DataServiceQuery<Title>> CreateDirectedInConverter()
        {
            return new ValueConverter<Person, DataServiceQuery<Title>>((p) => (DataServiceQuery<Title>)(
                   from _p in _context.People.IncludeTotalCount()
                   from _t in _p.TitlesDirected
                   where _p.Id == p.Id
                   select _t));
        }

#endregion

    }
}